home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 February: Technology Seed / Mac Tech Seed Feb '97.toast / ODF Release 3 / ODFDev / Bitmap / Sources / Part.cpp < prev    next >
Encoding:
Text File  |  1996-12-16  |  17.9 KB  |  576 lines  |  [TEXT/MPS ]

  1. //========================================================================================
  2. //
  3. //    File:                Part.cpp
  4. //    Release Version:    $ ODF 3 $
  5. //
  6. //    Author:                Henri Lamiraux
  7. //
  8. //    Copyright:    (c) 1993 - 1996 by Apple Computer, Inc., all rights reserved.
  9. //
  10. //========================================================================================
  11.  
  12. #include "Bitmap.hpp"
  13.  
  14. #ifndef PART_H
  15. #include "Part.h"
  16. #endif
  17.  
  18. #ifndef BINDING_K
  19. #include "Binding.k"
  20. #endif
  21.  
  22. #ifndef FRAME_H
  23. #include "Frame.h"
  24. #endif
  25.  
  26. #ifndef SELECT_H
  27. #include "Select.h"
  28. #endif
  29.  
  30. #ifndef CONTENT_H
  31. #include "Content.h"
  32. #endif
  33.  
  34. #ifndef DEFINES_K
  35. #include "Defines.k"
  36. #endif
  37.  
  38. // ----- Part Layer -----
  39.  
  40. #ifndef FWUTIL_H
  41. #include "FWUtil.h"
  42. #endif
  43.  
  44. #ifndef FWPRTITE_H
  45. #include "FWPrtIte.h"
  46. #endif
  47.  
  48. #ifndef FWITERS_H
  49. #include "FWIters.h"
  50. #endif
  51.  
  52. #ifndef FWABOUT_H
  53. #include "FWAbout.h"
  54. #endif
  55.  
  56. #ifndef FWPRESEN_H
  57. #include "FWPresen.h"
  58. #endif
  59.  
  60. // ----- OS Layer -----
  61.  
  62. #ifndef FWSUSINK_H
  63. #include "FWSUSink.h"
  64. #endif
  65.  
  66. #ifndef FWPICTUR_H
  67. #include "FWPictur.h"
  68. #endif
  69.  
  70. #ifndef FWEVENT_H
  71. #include "FWEvent.h"
  72. #endif
  73.  
  74. #ifndef FWBARRAY_H
  75. #include "FWBArray.h"
  76. #endif
  77.  
  78. #ifndef FWFILEAC_H
  79. #include "FWFileAc.h"
  80. #endif
  81.  
  82. #ifndef SLMixOS_H
  83. #include "SLMixOS.h"
  84. #endif
  85.  
  86. // ----- Foundation Layer -----
  87.  
  88. #ifndef FWSTREAM_H
  89. #include "FWStream.h"
  90. #endif
  91.  
  92. #ifndef FWMEMORY_H
  93. #include "FWMemory.h"
  94. #endif
  95.  
  96. #ifndef FWSUSINK_H
  97. #include "FWSUSink.h"
  98. #endif
  99.  
  100. // ----- OpenDoc Includes -----
  101.  
  102. #ifndef SOM_Module_OpenDoc_StdProps_defined
  103. #include <StdProps.xh>
  104. #endif
  105.  
  106. #ifndef SOM_ODTranslation_xh
  107. #include <Translt.xh>
  108. #endif
  109.  
  110. #ifndef SOM_ODSession_xh
  111. #include <ODSessn.xh>
  112. #endif
  113.  
  114. // ----- Macintosh Includes -----
  115.  
  116. #if defined(FW_BUILD_MAC) && !defined(__DRAG__)
  117. #include <Drag.h>
  118. #endif
  119.  
  120. // ----- Cyberdog Support -----
  121.  
  122. #if FW_SUPPORTS_CYBERDOG
  123. #include <Cyberdog.h>
  124. #include <CyberItem.xh>
  125. #include <CyberSession.xh>
  126. #include <CyberStream.xh>
  127. #include <ParameterSet.xh>
  128.  
  129. #ifndef SOM_CyberServiceMenu_xh
  130. #include <CyberServiceMenu.xh>
  131. #endif
  132.  
  133. #ifndef FWCYPART_H
  134. #include "FWCyPart.h"
  135. #endif
  136.  
  137. #ifndef FWCYSTRM_H
  138. #include "FWCyStrm.h"
  139. #endif
  140.  
  141. #include "FWSaveAs.h"
  142. #include "odfjpeg.h"
  143. #endif
  144.  
  145. //========================================================================================
  146. //    Runtime Information
  147. //========================================================================================
  148.  
  149. #ifdef FW_BUILD_MAC
  150. #pragma segment odfbitmap
  151. #endif
  152.  
  153. //========================================================================================
  154. //    class CBitmapPart
  155. //========================================================================================
  156.  
  157. FW_DEFINE_AUTO(CBitmapPart)
  158.  
  159. //----------------------------------------------------------------------------------------
  160. //    CBitmapPart::CBitmapPart
  161. //----------------------------------------------------------------------------------------
  162. //    CBitmapPart constructor
  163.  
  164. CBitmapPart::CBitmapPart(ODPart* odPart) :
  165.     FW_CPart(odPart, FW_gInstance, kPartInfoID),
  166. #if FW_SUPPORTS_CYBERDOG
  167.     fHelper (kODNULL),
  168.     fReloadContinuously (false),
  169. #endif
  170.     fBitmapContent(NULL),
  171.     fBitmapPresentation(NULL)
  172. {
  173.     FW_END_CONSTRUCTOR
  174. }
  175.  
  176. //----------------------------------------------------------------------------------------
  177. //    CBitmapPart::~CBitmapPart
  178. //----------------------------------------------------------------------------------------
  179. // CBitmapPart destructor
  180.  
  181. CBitmapPart::~CBitmapPart()
  182. {
  183.     FW_START_DESTRUCTOR
  184. }
  185.  
  186. //----------------------------------------------------------------------------------------
  187. //    CBitmapPart::Initialize
  188. //----------------------------------------------------------------------------------------
  189. //    ODF Method
  190.  
  191. void CBitmapPart::Initialize(Environment* ev, ODStorageUnit* storageUnit, FW_Boolean fromStorage)
  192. {
  193.     // ----- Call first inherited -----
  194.     FW_CPart::Initialize(ev, storageUnit, fromStorage);
  195.     
  196. #if FW_SUPPORTS_CYBERDOG
  197. /*
  198.     ----- Cyberdog: Implementing your Cyberdog Part Extension -----
  199.     
  200.     The first thing you want to do is to add support for the CyberPartExtension 
  201.     (for details on this, see chapter 4 of the Cyberdog Programmer’s Guide, 
  202.     "Creating a Cyberdog Display Part", under the heading "Implementing your 
  203.     Cyberdog Part Extension").
  204.     
  205.     ODF encapsulates all the CyberPartExtension support in the 
  206.     FW_CCyberdogHelper class. You’ll want to create an instance of 
  207.     FW_CCyberdogHelper along with your part. Create the instance in your part’s 
  208.     Initialize method, like so:
  209. */
  210.     fHelper = ::FW_SupportCyberdogIfPresent (ev, this, FW_kUseCyberMenus, cCyberdogCommands, FW_kUseNavigator);
  211. /*
  212.     FW_SupportCyberdogIfPresent returns null if Cyberdog is not installed.
  213.     It is NOT necessary to delete the helper because it is an eventhandler
  214.     and will be deleted by FW_CPart automatically.
  215.  
  216.     You may choose whether you want the Cyberdog menus displayed in your part or 
  217.     not (FW_kUseCyberMenus); you may also choose whether you want your part to 
  218.     be displayed in the Cyberdog navigator/browser (FW_kUseNavigator).
  219.     
  220.     If you want the Cyberdog menus then you must reserve a range of command 
  221.     numbers for them as well. ODFBitmap defines cCyberdogCommands in Defines.k.
  222.     
  223.     ----- Creating a Stream for Downloading -----
  224.     
  225.     When a Cyberdog item is opened, Cyberdog calls your part extension's 
  226.     SetCyberItem method. ODF will then call the helper's DoSetCyberItem
  227.     method. You may register a function to be called automatically. Here 
  228.     "LoadCyberItem" will be called, and "this" will be passed to it.
  229.     Notice that for ODFBitmap, LoadCyberItem is a static member function, not
  230.     a global function, but it makes no difference.
  231. */
  232.     if (fHelper)
  233.         fHelper->SetLoadCyberItemThreadProcedure (LoadCyberItem, this);
  234. #endif
  235.     
  236.     // ----- Register our Presentations
  237.     fBitmapPresentation = RegisterPresentation(ev, 
  238.                                             "Apple:Presentation:ODFBitmap", 
  239.                                             TRUE, 
  240.                                             FW_NEW(CBitmapSelection, (ev, this, fBitmapContent)));
  241.  
  242.     // ----- Register our other kinds -----
  243.     RegisterKind(ev, kPICTOSType, kODPlatformDataType, FW_kDataInterchangeStorage, FW_kImportExportEnabled);
  244.     RegisterKind(ev, kPICTOSType, kODPlatformFileType, FW_kAllStorage, FW_kImport);
  245.     RegisterKind(ev, kJPEGOSType, kODPlatformDataType, FW_kDataInterchangeStorage, FW_kImport);
  246.     RegisterKind(ev, kJPEGOSType, kODPlatformFileType, FW_kAllStorage, FW_kImport);
  247.     RegisterKind(ev, kJFIFOSType, kODPlatformDataType, FW_kDataInterchangeStorage, FW_kImport);
  248.     RegisterKind(ev, kJFIFOSType, kODPlatformFileType, FW_kAllStorage, FW_kImport);
  249. }
  250.  
  251. //----------------------------------------------------------------------------------------
  252. //    CBitmapPart::NewDocumentWindow
  253. //----------------------------------------------------------------------------------------
  254.  
  255. FW_CWindow* CBitmapPart::NewDocumentWindow(Environment* ev)
  256. {    
  257.     FW_CRect bitmapRect;
  258.     fBitmapContent->GetBitmap(ev).GetBitmapBounds(bitmapRect);    // return in 72 dpi
  259. #ifdef FW_BUILD_MAC
  260.     // Leave some room for the grow box
  261.     bitmapRect.Inset(FW_IntToFixed(-16), FW_IntToFixed(-16));
  262. #endif
  263.     
  264.     FW_CRect screenBounds;
  265.     ::FW_GetMainScreenBounds(screenBounds);
  266.     screenBounds.Inset(FW_IntToFixed(3), FW_IntToFixed(3));
  267.     
  268.     return new FW_CWindow(ev,
  269.                         this,
  270.                          FW_CPart::fgViewAsFrameToken,
  271.                         fBitmapPresentation,
  272.                         bitmapRect.Size(),
  273.                         screenBounds.TopLeft(),
  274.                         FW_kDocumentWindow);
  275. }
  276.  
  277. //----------------------------------------------------------------------------------------
  278. //    CBitmapPart::NewPartContent
  279. //----------------------------------------------------------------------------------------
  280. //    ODF Method
  281.  
  282. FW_CContent* CBitmapPart::NewPartContent(Environment* ev)
  283. {
  284.     fBitmapContent = FW_NEW(CBitmapContent, (ev, this));
  285.     return fBitmapContent;
  286. }
  287.  
  288. #if FW_SUPPORTS_CYBERDOG
  289.  
  290.  
  291. //----------------------------------------------------------------------------------------
  292. //    CBitmapPart::DoAdjustMenus
  293. //    See "Cyberdog: Live Data" below in DoMenu.
  294. //----------------------------------------------------------------------------------------
  295.  
  296. FW_Handled CBitmapPart::DoAdjustMenus(Environment* ev, FW_CMenuBar* menuBar, FW_Boolean hasMenuFocus, FW_Boolean isRoot)
  297. {
  298. FW_UNUSED(isRoot);
  299.     if (hasMenuFocus && fHelper) {
  300.         menuBar->EnableCommand (ev, cReload, fHelper->GetCyberItem(ev) != kODNULL);
  301.         menuBar->EnableAndCheckCommand (ev, cReloadFast, TRUE, fReloadContinuously);    
  302.     }
  303.     
  304.     return FW_kNotHandled;
  305. }
  306.  
  307. #endif
  308.  
  309. //----------------------------------------------------------------------------------------
  310. //    CBitmapPart::DoAbout
  311. //----------------------------------------------------------------------------------------
  312.  
  313. FW_Handled CBitmapPart::DoAbout(Environment* ev)
  314. {
  315.     ::FW_About(ev, this, kAbout);
  316.     
  317.     return FW_kHandled;
  318. }
  319.  
  320. //----------------------------------------------------------------------------------------
  321. //    CBitmapPart::DoMenu
  322. //----------------------------------------------------------------------------------------
  323. // ODF method
  324.  
  325. #if FW_SUPPORTS_CYBERDOG
  326.  
  327. FW_Handled CBitmapPart::DoMenu(Environment* ev, const FW_CMenuEvent& theMenuEvent)
  328. {
  329.     FW_Handled result = FW_kHandled;
  330.     
  331.     ODCommandID id = theMenuEvent.GetCommandID(ev);
  332. /*
  333.     ----- Cyberdog: Live Data -----
  334.     
  335.     This is a cute feature which you may be able to put to real use.
  336.     The Cyberdog Navigator has a "reload" command but it actually loads a new
  337.     part (Cyberdog never calls you part to download data more than once). This 
  338.     Reload command will cause your part to open its CyberItem and download 
  339.     its data again (which may of course be updated data). A stock ticker
  340.     part could make use of this for example.
  341.     
  342.     Unfortunately Bitmap flashes but that could be solved by offscreen 
  343.     buffering. And "Reload Continuously" would be improved by a Sleep delay.
  344. */
  345.     if (id == cReload && fHelper) {
  346.         fHelper->GetExtension(ev)->SetCyberItem (ev, fHelper->GetCyberItem(ev), kODNULL);
  347.     }
  348.     else if (id == cReloadFast && fHelper) {
  349.         fReloadContinuously = !fReloadContinuously;
  350.     }
  351.     else if (id == kODCommandSaveACopy && fHelper) {
  352. /*
  353.     ----- Cyberdog: Embedding in the Navigator (SaveAs) -----
  354.     
  355.     You would think that OpenDoc would handle SaveAs. It does, but it can
  356.     only save another OD document. When we're doing browsing we frequently have
  357.     pictures, etc, which we want to save as normal MacOS documents. Until
  358.     OpenDoc supports multi-kind SaveAs, parts must do that themselves.
  359.     
  360.     Also, if we have a navigator part at the root, we might not want to save
  361.     it. Therefore there are two situations where you need to use this SaveAs
  362.     code: (1) Navigator is root part (2) You have multiple export kinds.
  363.  
  364.     In fact you **MUST** support SaveAs anyway if you want to be embeddable in 
  365.     the Navigator, because Cyberdog disables the OpenDoc Save mechanism.
  366.     
  367.     Basic question: Why would you implement your own Save As code instead of using 
  368.     OpenDocs? Because...
  369.     (1) you want to export files as standard MacOS documents (not OD documents).
  370.     (2) you don't want the navigator (browser) saved as the root part of the 
  371.     document.
  372.  
  373.     The new logic is to use the custom Save As code whenever we're in a 
  374.     "Cyberdog environment". That is...
  375.     (1) navigator is the root part.
  376.     (2) you are the root part but are in a Cyberdog draft.
  377.  
  378.     Note that being in a Cyberdog draft does not imply being in a "Cyberdog 
  379.     environment". For example, one could have ODFDraw, with a navigator embedded 
  380.     in it, and your part in the navigator. Your part would be in a Cyberdog 
  381.     draft but it would be important to use the default OD mechanism (since you 
  382.     want to save the whole document, not just your part).
  383.  
  384.     Note also that it's possible to be an embedded part in a part in the 
  385.     navigator. The logic doesn't handle this yet, but Cyberdog doesn't do 
  386.     embedded yet either, so it's not a problem which must be solved for R2.
  387. */
  388.         CyberSession* session = GetCyberSession(ev);
  389.         FW_Boolean inCyberDraft = session->IsInCyberDraft (ev, GetODPart(ev));
  390.         FW_CFrame* probableFrame = GetLastActiveFrame(ev);
  391.         FW_Boolean isRoot = probableFrame->IsRoot(ev);
  392.         FW_Boolean isInRootNavigator = false;
  393.         if (!isRoot) {
  394.             FW_CAcquiredODPart nav = fHelper->AcquireContainingNavigator (ev);
  395.             FW_Boolean isInNavigator = (nav != kODNULL);
  396.             if (isInNavigator)
  397.                 isInRootNavigator = session->IsContainedInRootNavigatorPart (ev, probableFrame->GetODFrame(ev));
  398.         }
  399.         FW_Boolean handle = (isInRootNavigator || (isRoot && inCyberDraft));
  400.         if (!handle)
  401.             return FW_kNotHandled;
  402.         // Get a default name
  403.         FW_PFileSpecification whichFile (ev, "");
  404.         short whichKind;
  405.         FW_CString defaultName;
  406.         if (fHelper && fHelper->GetCyberItem(ev)) {
  407.             Str255 itemName;
  408.             ScriptCode dummy;
  409.             fHelper->GetCyberItem(ev)->GetStringProperty (ev, kCDFileName, itemName, &dummy);
  410.             defaultName.ReplaceAll (itemName);
  411.         }
  412.         // Ask
  413.         Boolean doIt = ::FW_AskSaveAs (kSaveAs, defaultName, whichFile, whichKind);
  414.         if (doIt) {
  415.             // Can't do OpenDoc or CyberItem yet
  416.             if (whichKind == 1 || whichKind == 2) {
  417.                 FW_ASSERT (("Sorry, not yet implemented!", false));
  418.                 FW_Failure (FW_xUnknownError);
  419.             }
  420.             // Set up Picture file
  421. #ifdef FW_BUILD_MAC
  422.             whichFile->MacSetTypeAndCreator (ev, 'PICT', 'JVWR');
  423.             if (FW_FileSystem_IsValidFile (ev, whichFile))
  424.                 FW_FileSystem_DeleteFile (ev, whichFile);
  425. #endif
  426.             FW_FileSystem_CreateFile (ev, whichFile, true);
  427.             FW_CAccessPermission permission (FW_kWrite);
  428.             FW_PFile file (ev, whichFile, permission, true); 
  429.             FW_PFileSink sink (ev, file);
  430.             FW_CWritableStream stream (sink);
  431.             // Write it out
  432. #ifdef FW_BUILD_MAC
  433.             // PICT format is wierd. 512 bytes of nothing?
  434.             char dummy [512];
  435.             memset (dummy, 0, 512);
  436.             sink->Write (ev, dummy, 512);
  437. #endif
  438.             FW_CPicture picture = fBitmapContent->GetBitmap(ev).MacGetAsPicture();
  439.             FW_PlatformPict platformPict = picture.GetPlatformPict();
  440.             unsigned long pictSize = FW_CMemoryManager::GetSystemHandleSize((FW_PlatformHandle)platformPict);
  441.             FW_CAcquireLockedSystemHandle lockedHandle((FW_PlatformHandle)platformPict);
  442.             stream.Write(lockedHandle.GetPointer(), pictSize);    
  443.         }
  444.     }
  445.     else
  446.         result = FW_kNotHandled;
  447.         
  448.     return result;
  449. }
  450.  
  451. #endif
  452.  
  453. //----------------------------------------------------------------------------------------
  454. // CBitmapPart::UpdatePresentation
  455. //----------------------------------------------------------------------------------------
  456.  
  457. void CBitmapPart::UpdatePresentation(Environment* ev)
  458. {
  459.     // ----- Force all display frames to be redrawn
  460.     fBitmapPresentation->Invalidate(ev);
  461.     
  462.     // ----- Force thumbnail to be recalculated -----
  463.     fBitmapPresentation->UpdateViewAs(ev);
  464. }
  465.  
  466. //----------------------------------------------------------------------------------------
  467. //    CBitmapPart::NewFrame
  468. //----------------------------------------------------------------------------------------
  469. // ODF Method
  470.  
  471. FW_CFrame* CBitmapPart::NewFrame(Environment* ev,
  472.                                  ODFrame* odFrame, 
  473.                                  FW_CPresentation* presentation,
  474.                                     FW_Boolean fromStorage)
  475. {
  476.     FW_UNUSED(fromStorage);
  477.     FW_ASSERT(presentation == fBitmapPresentation);
  478.  
  479.     return FW_NEW(CBitmapFrame, (ev, odFrame, presentation, this, fBitmapContent));
  480. }
  481.  
  482. //----------------------------------------------------------------------------------------
  483. //    CBitmapPart::AdjustFramesSize
  484. //----------------------------------------------------------------------------------------
  485. //    Bitmap Part Method
  486.  
  487. void CBitmapPart::AdjustFramesSize(Environment* ev)
  488. {
  489.     FW_CPartFrameIterator ite(ev, this);
  490.     for (CBitmapFrame* frame = (CBitmapFrame*)ite.First(ev); ite.IsNotComplete(ev); frame = (CBitmapFrame*)ite.Next(ev))
  491.     {
  492.         frame->AdjustFrameSize(ev);
  493.     }
  494. }
  495.  
  496. #if FW_SUPPORTS_CYBERDOG
  497. //----------------------------------------------------------------------------------------
  498. // CBitmapPart::FocusAcquiredInFrame
  499. //----------------------------------------------------------------------------------------
  500. // This code allows Cyberdog "services" to put up their menus. You'll need
  501. // to copy this into your Part subclass.
  502.  
  503. void CBitmapPart::FocusAcquiredInFrame(Environment* ev, ODTypeToken focus, FW_CFrame* frame)
  504. {
  505.     FW_CPart::FocusAcquiredInFrame (ev, focus, frame);
  506.     
  507.     if (focus == FW_CPart::fgMenuFocusToken) {
  508.         FW_CCyberdogHelper* helper = GetCyberdogHelper();
  509.         if (helper && helper->GetCyberServiceMenu())
  510.             helper->GetCyberServiceMenu()->MenuFocusAcquired (ev, frame->GetODFrame(ev));
  511.     }
  512. }
  513. #endif
  514.  
  515. #if FW_SUPPORTS_CYBERDOG
  516. //----------------------------------------------------------------------------------------
  517. // CBitmapPart::FocusLostInFrame
  518. //----------------------------------------------------------------------------------------
  519. // This code allows Cyberdog "services" to put up their menus. You'll need
  520. // to copy this into your Part subclass.
  521.  
  522. void CBitmapPart::FocusLostInFrame(Environment* ev, ODTypeToken focus, FW_CFrame* frame, FW_Boolean samePart)
  523. {
  524.     FW_CPart::FocusLostInFrame (ev, focus, frame, samePart);
  525.     
  526.     if (samePart && focus == FW_CPart::fgMenuFocusToken) {
  527.         FW_CCyberdogHelper* helper = GetCyberdogHelper();
  528.         if (helper && helper->GetCyberServiceMenu())
  529.             helper->GetCyberServiceMenu()->MenuFocusLost (ev, frame->GetODFrame(ev));
  530.     }
  531. }
  532. #endif
  533.  
  534. #if FW_SUPPORTS_CYBERDOG
  535. //----------------------------------------------------------------------------------------
  536. // CBitmapPart::LoadCyberItem
  537. //----------------------------------------------------------------------------------------
  538.  
  539. void CBitmapPart::LoadCyberItem (Environment* ev, void* selfCPart)
  540. {
  541. /*
  542.     ----- Cyberdog: Creating a Stream for Downloading -----
  543.     
  544.     This static method will be called automatically when a Cyberdog item is 
  545.     opened. It has been registered via SetLoadCyberItemThreadProcedure, which
  546.     creates a new thread before calling this method. Since LoadCyberItem is
  547.     running in its own thread, it can wait for data to arrive without holding
  548.     up the user interface.
  549.     
  550. */
  551.     // Note this is a static method. "this" is passed in from Initialize
  552.     CBitmapPart* self = (CBitmapPart*) selfCPart;
  553.     
  554.     CyberItem* item = self->fHelper->GetCyberItem(ev);
  555.     
  556.     // Support continuous downloads
  557.     // We could do it by re-spawning our thread, but that causes problems
  558.     // with the smart pointer (can't create a new thread while the original
  559.     // one still exists). Therefore we'll just loop in the current thread.
  560.     do {
  561.     
  562.     CyberStream* cStream = item->CreateCyberStream (ev);
  563.     cStream->Open (ev);
  564.     FW_PCyberSink sink (ev, cStream);
  565.     
  566.     FW_CBitmap b = ODF_JPEG_Read (ev, sink);
  567.     self->fBitmapContent->SetBitmap (ev, b);
  568.     self->fBitmapPresentation->Invalidate(ev);
  569.     
  570.     }
  571.     while (self->fReloadContinuously);
  572. }
  573.  
  574. #endif // FW_SUPPORTS_CYBERDOG
  575.  
  576.